﻿using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace NewRetail.Application.Utility.Hubs
{
    #region 服务端接口
    /// <summary>
    /// 服务端接口
    /// </summary>
    public interface IServerNotifyHub
    {
        Task SendLogin(string username);
    }
    #endregion

    #region 客户端使用的接口
    /// <summary>
    /// 客户端使用的接口，服务端调用客户端的OnNotify、OnLine等方法
    /// </summary>
    public interface IClientNotifyHub
    {
        /// <summary>
        /// SignalR接收信息
        /// </summary>
        /// <param name="data">信息内容</param>
        /// <returns></returns>
        Task OnNotify(object data);
        /// <summary>
        /// SignalR接收信息
        /// </summary>
        /// <param name="data">信息内容</param>
        /// <returns></returns>
        Task OnLine(object data);
        /// <summary>
        /// SignalR接收信息
        /// </summary>
        /// <param name="data">信息内容</param>
        /// <returns></returns>
        Task OffLine(object data);
        /// <summary>
        /// SignalR接收信息
        /// </summary>
        /// <param name="message">信息内容</param>
        /// <returns></returns>
        Task ReceiveMessage(object message);
        Task ReceiveDashboardMessage(string method, object message);
    }
    #endregion

    public class NewRetailHub : Hub<IClientNotifyHub>, IServerNotifyHub
    {
        public ILogger<NewRetailHub> _logger;
        private readonly ISignalrMemoryCacheHelper _signalrMemoryCacheHelper;
        private readonly ISignalrDealHelper _signalrDealHelper;

        public NewRetailHub(ISignalrMemoryCacheHelper signalrMemoryCacheHelper, ISignalrDealHelper signalrDealHelper, ILogger<NewRetailHub> logger)
        {
            _signalrMemoryCacheHelper = signalrMemoryCacheHelper;
            _signalrDealHelper = signalrDealHelper;
            _logger = logger;
        }

        #region 客户端调用服务端的SendLogin方法
        /// <summary>
        /// 客户端调用的方法
        /// </summary>
        /// <param name="username"></param>
        public async Task SendLogin(string username)
        {
            Console.WriteLine("SendLogin:" + username);
            await GetOnLineCount();
            await GetDashboardData();
        }
        #endregion

        #region OnConnectedAsync
        public override async Task OnConnectedAsync()
        {
            //await Clients.All.OnNotify(new { UserId= Context.User.Identity.Name, Name=Context.User.Identity.Name, ConnectId = Context.ConnectionId });
            var connId = Context.ConnectionId;
            var identity = (ClaimsIdentity)Context.User.Identity;
            if (identity.Claims.Count() > 0)
            {
                var userId = "fe";// identity.Claims.FirstOrDefault(u => u.Type == JwtClaimTypes.Name).Value;
                var groups = Context.GetHttpContext().Request.Query["group"].FirstOrDefault();
                _logger.LogDebug($"OnConnectedAsync----userId:{userId},groups:{groups},connectionId:{ connId}");
                if (!string.IsNullOrWhiteSpace(userId))
                {
                    //await _signalrRedisHelper.AddConnectForUserAsync(userId, Context.ConnectionId);
                    _signalrMemoryCacheHelper.AddConnectForUser(userId, connId);
                    await JoinToGroup(userId, connId, groups?.Split(','));
                    await DealOnLineNotify(userId, connId);
                }
            }
            else
            {
                await DealOnLineNotifyNoAuthentication(connId);
            }

            await base.OnConnectedAsync();
        }
        #endregion

        #region OnDisconnectedAsync
        public override async Task OnDisconnectedAsync(Exception exception)
        {
            //var userId = Context.User.Identity.Name;
            var connId = Context.ConnectionId;
            var identity = (ClaimsIdentity)Context.User.Identity;
            if (identity.Claims.Count() > 0)
            {
                var userId = "fe";// identity.Claims.FirstOrDefault(u => u.Type == JwtClaimTypes.Name).Value;
                var groups = Context.GetHttpContext().Request.Query["group"].FirstOrDefault();
                _logger.LogDebug($"OnDisconnectedAsync----userId:{userId},groups:{groups},connectionId:{ connId}");
                if (!string.IsNullOrWhiteSpace(userId))
                {
                    //await _signalrRedisHelper.RemoveConnectForUserAsync(userId, Context.ConnectionId);
                    _signalrMemoryCacheHelper.RemoveConnectForUser(userId, connId);
                    await DealOffLineNotify(userId, connId);
                }
                await LeaveFromGroup(connId, groups?.Split(','));
            }
            else
            {
                await DealOffLineNotifyNoAuthentication(connId);
            }
            await base.OnDisconnectedAsync(exception);
        }
        #endregion

        #region 加入组
        /// <summary>
        /// 加入组
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="connectionId"></param>
        /// <param name="groups"></param>
        /// <returns></returns>
        private async Task JoinToGroup(string userId, string connectionId, params string[] groups)
        {
            if (!string.IsNullOrWhiteSpace(userId) && groups != null && groups.Length > 0)
            {
                foreach (var group in groups)
                {
                    await Groups.AddToGroupAsync(connectionId, group);
                    //await _signalrRedisHelper.AddUserForGroupAsync(group, connectionId, userId);
                    _signalrMemoryCacheHelper.AddUserForGroup(group, connectionId, userId);

                    // await Clients.Group(group).OnJoinGroup(new { ConnectId = connectionId, UserId = userId, GroupName = group });
                }
            }
        }
        #endregion

        #region 从组中移除
        /// <summary>
        /// 从组中移除
        /// </summary>
        /// <param name="connectionId"></param>
        /// <param name="groups"></param>
        /// <returns></returns>
        private async Task LeaveFromGroup(string connectionId, params string[] groups)
        {
            if (groups != null && groups.Length > 0)
            {
                foreach (var group in groups)
                {
                    await Groups.RemoveFromGroupAsync(connectionId, group);
                    _signalrMemoryCacheHelper.RemoveConnectFromGroup(group, connectionId);
                    //await _signalrRedisHelper.RemoveConnectFromGroupAsync(group, connectionId);
                    // await Clients.Group(group).OnLeaveGroup(new { ConnectId = connectionId, GroupName = group });
                }
            }
        }
        #endregion

        #region 处理上线通知(只有用户第一个连接才通知)
        /// <summary>
        /// 处理上线通知(只有用户第一个连接才通知)
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="connectionId"></param>
        /// <returns></returns>
        private async Task DealOnLineNotify(string userId, string connectionId)
        {
            //var userConnectCount = await _signalrRedisHelper.GetConnectsCountByUserAsync(userId);
            var userConnectCount = _signalrMemoryCacheHelper.GetConnectsCountByUser(userId);

            var real = _signalrDealHelper.IsOnline(userId);
            var connCount = _signalrDealHelper.ClientsCount();
            var client = new SignalrOnOrOffLineData
            {
                UserId = userId,
                ConnectionId = connectionId,
                IdentityName = userId,
                ConnecServerTime = DateTime.Now,
                ConnCount = connCount,
                IsFirst = userConnectCount == 1
            };
            if (real == null)
                _signalrDealHelper.AddClient(connectionId, client);
            else
            {
                //1、移除
                _signalrDealHelper.RemoveClient(real.ConnectionId);
                //2、新增
                _signalrDealHelper.AddClient(connectionId, client);
            }

            var result = _signalrDealHelper.GetClients().Values.ToArray();

            await Clients.All.OnLine(result);
        }

        #endregion

        #region 处理下线通知(只有当用户一个连接都没了 才算下线)
        /// <summary>
        /// 处理下线通知(只有当用户一个连接都没了 才算下线)
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="connectionId"></param>
        /// <returns></returns>
        private async Task DealOffLineNotify(string userId, string connectionId)
        {
            //var userConnectCount = await _signalrRedisHelper.GetConnectsCountByUserAsync(userId);
            var userConnectCount = _signalrMemoryCacheHelper.GetConnectsCountByUser(userId);
            _signalrDealHelper.RemoveClient(connectionId);
            var client = new SignalrOnOrOffLineData
            {
                UserId = userId,
                ConnectionId = connectionId,
                IdentityName = userId,
                ConnecServerTime = DateTime.Now,
                IsLast = userConnectCount == 0
            };
            var result = _signalrDealHelper.GetClients().Values.ToArray();
            await Clients.All.OffLine(result);
        }
        #endregion

        #region 获取在线用户数
        public async Task GetOnLineCount()
        {
            var onLineNum = _signalrDealHelper.ClientsCount();
            await Clients.All.ReceiveMessage(onLineNum);
        }
        #endregion

        #region 获取dashboard数据
        public async Task GetDashboardData()
        {
            //基础信息概况
            var basicInfoSummary = await _signalrDealHelper.GetBasicInfoSummary();
            await Clients.All.ReceiveDashboardMessage("baseInfoSummary", basicInfoSummary);
            //今日销售
            var todaySale = await _signalrDealHelper.GetTodaySale();
            await Clients.All.ReceiveDashboardMessage("todaySale", todaySale);
            //近7日销售
            var lastSevenDaySale = await _signalrDealHelper.GetLastSevenDaySale();
            await Clients.All.ReceiveDashboardMessage("lastSevenDaySale", lastSevenDaySale);
            //本年度销售
            var yearSale = await _signalrDealHelper.GetYearSale();
            await Clients.All.ReceiveDashboardMessage("yearSale", yearSale);
            //今日各门店销售
            var todayStoreSale = await _signalrDealHelper.GetTodayStoreSale();
            await Clients.All.ReceiveDashboardMessage("todayStoreSale", todayStoreSale);
            //今日支付方式占比数据
            var todayPayWaySale = await _signalrDealHelper.GetPaymentWaySale();
            await Clients.All.ReceiveDashboardMessage("todayPayWaySale", todayPayWaySale);
            //今日购买渠道占比数据
            var todayBuyChannelSale = await _signalrDealHelper.GetBuyChannelSale();
            await Clients.All.ReceiveDashboardMessage("todayBuyChannelSale", todayBuyChannelSale);
            //测试
            var result = _signalrDealHelper.GetDashboardTest();
            await Clients.All.ReceiveDashboardMessage("dtb", result);
        }
        #endregion


        #region 处理上线通知(不需要认证)
        /// <summary>
        /// 处理上线通知(不需要认证)
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="connectionId"></param>
        /// <returns></returns>
        private async Task DealOnLineNotifyNoAuthentication(string connectionId)
        {
            var userConnectCount = _signalrMemoryCacheHelper.GetConnectsCountByUser(connectionId);
            var real = _signalrDealHelper.IsOnline(connectionId);
            var connCount = _signalrDealHelper.ClientsCount();
            var client = new SignalrOnOrOffLineData
            {
                UserId = connectionId,
                ConnectionId = connectionId,
                IdentityName = connectionId,
                ConnecServerTime = DateTime.Now,
                ConnCount = connCount,
                IsFirst = userConnectCount == 1
            };
            if (real == null)
                _signalrDealHelper.AddClient(connectionId, client);
            else
            {
                //1、移除
                _signalrDealHelper.RemoveClient(real.ConnectionId);
                //2、新增
                _signalrDealHelper.AddClient(connectionId, client);
            }

            var result = _signalrDealHelper.GetClients().Values.ToArray();
            await Clients.All.OnLine(result);
        }

        #endregion

        #region 处理下线通知(不需要认证)
        /// <summary>
        /// 处理下线通知(不需要认证)
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="connectionId"></param>
        /// <returns></returns>
        private async Task DealOffLineNotifyNoAuthentication(string connectionId)
        {
            //var userConnectCount = await _signalrRedisHelper.GetConnectsCountByUserAsync(userId);
            var userConnectCount = _signalrMemoryCacheHelper.GetConnectsCountByUser(connectionId);
            _signalrDealHelper.RemoveClient(connectionId);
            var client = new SignalrOnOrOffLineData
            {
                UserId = connectionId,
                ConnectionId = connectionId,
                IdentityName = connectionId,
                ConnecServerTime = DateTime.Now,
                IsLast = userConnectCount == 0
            };
            var result = _signalrDealHelper.GetClients().Values.ToArray();
            await Clients.All.OffLine(result);
        }
        #endregion

        #region
        public async Task SendMessage(string message)
        {
            await Clients.All.ReceiveMessage(message);
        }
        #endregion
    }
}
